Skip to main content

Shell arrays

Let\'s do some shell again! Shell is a tool that allows you to interact with your system. You will have to use it all along this year and certainly after. So you must know how to use it correctly!

Arrays

As you know, it is possible to declare variables in shell and to use them in your commands. As a reminder, declaring and using a variable is done like this:

42sh$ my_var='I am a shell variable'
42sh$ echo "$my_var"
I am a shell variable

But sometimes, you need to store several information, which are all related. In C you would have done that with an array. In shell, the first solution you may find is to do the following:

42sh$ array0=first
42sh$ array1=second
42sh$ array2=third

It is a solution that can be used as if we had an array but actually, it creates a single variable for each index of the array. So it takes as much names as indices you have.

The real solution is shell arrays.

This feature isn\'t specified by POSIX but is supported by ksh/bash/zsh

Declaration

There are several syntaxes to declare an array in shell. The first one is done by declaring each entry of the array separately. The syntax is:

array_name[index]=content

42sh$ array[0]=first
42sh$ array[1]=second
42sh$ array[2]=third

This syntax is not very convenient but it can be useful in certain cases. Otherwise, you can declare all the array in one command using the following syntax:

[declare -a] array_name=(value1 value2 ... valueN)

The declare -a is not mandatory, it simply indicates that your variable must be treated as an array. All the following commands are correct.

42sh$ declare -a array
42sh$ array[0]=first
42sh$ array[1]=second
42sh$ array[2]=third
42sh$ echo ${array[-3]} # access to the array length - 3 index so 0 here
first
42sh$ declare -a array=(first second third) # explicit declare example
42sh$ array=([0]=zero [4]=fourth [2]=second) # you can also specify indexes

Use

Now that you know how to declare an array in shell, it\'s time to learn how to use it.

To get a value from the array, it is almost as simple as in C. To do so, the following syntax must be used (curly brackets are necessary):

${array_name[index]}

For example, if you want to print the value of the third element of the array, the following command will do the job:

42sh$ echo "${array[2]}"
third

The indices work like in C. The first element is at index 0. So the third element is placed at index 2.

So if you would like to print all the array, you would probably do a loop like so:

42sh$ for i in $(seq 0 2); do echo "${array[i]}"; done
first
second
third

But there is a way to do it a lot more easily:

echo "${array[*]}" or echo "${array[@]}"

Finally, getting the length of an array is as simple as the previous command. This is done like this:

42sh$ echo "${#array[@]}"
3

Note that the index value is evaluated as an arithmetic context, hence this is a valid code:

42sh$ three=3
42sh$ arr[two = 2, i = two + three]=hello # having fun with arithmetic
42sh$ arr[++i]=world
42sh echo "current index: $i"
current index: 6
42sh$ declare -p arr # let's check what's inside arr
declare -a arr='([5]="hello" [6]="world")'

Question 1

How to print the value contained at the index length - 3?

Answer: echo ${array[-3]}

Question 2

What is the syntax to get the length of an array?

Answer: ${#array[@]} or ${#array[*]}

Question 3

Can we have indices that are not contiguous in shell arrays?

Answer: Yes

Question 4

What is the option to declare that specify you create an array? And what are the advantages?

Answer: The option is -a and it specify the variable must be treated as an indexed array. It makes the use of this array faster and can throw error in case of bad use.

Exercise

After this exercise, you may realize that this command is very specific. It can only get the logins of the champions whose firstname starts with the letter B. Also, the files in which we read and write are fixed.

Let\'s make it generic!

Associative arrays

This part will be short because associative arrays work the same way as indexed arrays. The only difference is that you can use strings instead of indices.

Associative arrays need to be explicitly declared with declare -A, if not an indexed array will be created.

42sh$ declare -A map
42sh$ declare -A map=([one]=1 [two]=2 [three]=3)
42sh$ map=([one]=1 [two]=2 [three]=3)
42sh$ map[one]=1 map[two]=2 map[three]=3
42sh$ declare -p map
declare -A map='([one]="1" [two]="2" [three]="3" )'
42sh$ echo "${map[@]}"
1 2 3
42sh$ echo "${#map[@]}" # print the length of map
3

One feature that can be useful with associative arrays is that you can get the keys of your array. It works with previous arrays too but the usefulness is more obvious for associative arrays. The syntax is the following one:

${!array_name[@]}
# or
${!array_name[*]}

Example:

42sh$ declare -A map=([one]=1 [two]=2 [three]=3)
42sh$ echo "${map[@]}" # print the content
1 2 3
42sh$ echo "${#map[@]}" # print the length
3
42sh$ echo "${!map[@]}" # print the keys
one two three
42sh$ array=(first second third fourth)
42sh$ echo "${array[@]}" # print the content
first second third fourth
42sh$ echo "${#array[@]}" # print the length
4
42sh$ echo "${!array[@]}" # print the indices
0 1 2 3

If you want more information about arrays in shell, take a look at the section about arrays in man bash.

Question 1

Does it work if I declare an assoc array without using declare -A?

42sh$ map=([one]=1 [two]=2 [three]=3)

`Answer`: No, it creates an indexed array, thus indexes are considered as arithmetic values, so if $one, $two and $three are unset or don\'t contain any arithmetic reference (this refers to arithmetical infinite dereferencing, but this is an advanced notion) they will be evaluated to 0, so our assignement will be equivalent to:

42sh$ map=([0]=1 [0]=2 [0]=3)

Hence:

42sh$ map=([one]=1 [two]=2 [three]=3)
42sh$ declare -p map
declare -a map='([0]="3")'